import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTabWidget, QWidget, QVBoxLayout,
                             QHBoxLayout, QLabel, QPushButton, QFileDialog, QSlider,
                             QComboBox, QMessageBox, QGroupBox, QGridLayout, QAction,
                             QToolBar, QProgressBar, QLineEdit, QSpinBox, QFormLayout,
                             QDialog, QDialogButtonBox)
from PyQt5.QtGui import QPixmap, QImage, QFont, QCursor
from PyQt5.QtCore import Qt, QThread, pyqtSignal


# 视频处理线程类（增强版）
class VideoThread(QThread):
    change_pixmap_signal = pyqtSignal(np.ndarray)  # 发送帧更新信号
    finished_signal = pyqtSignal()  # 播放结束信号
    progress_signal = pyqtSignal(int)  # 进度条更新信号

    def __init__(self, video_path=None):
        super().__init__()
        self.video_path = video_path  # 视频路径（None表示摄像头）
        self._run_flag = True  # 线程运行标志
        self._pause_flag = False  # 暂停标志
        self._frame_pos = 0  # 当前帧位置
        self._total_frames = 0  # 总帧数
        self._fps = 30  # 帧率（默认值）
        self._speed = 1.0  # 播放速度
        self.processing_function = lambda frame: frame  # 默认处理函数
        self._skip_frames = 0  # 需要跳过的帧数
        self._processed_frames = 0  # 已处理的帧数
        self.cap = None  # 视频捕获对象

    def run(self):
        """线程主循环：读取视频帧并处理"""
        # 打开视频文件或摄像头
        if self.video_path:
            self.cap = cv2.VideoCapture(self.video_path)
            self._total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
            self._fps = self.cap.get(cv2.CAP_PROP_FPS) or 30  # 处理帧率为0的情况
        else:
            self.cap = cv2.VideoCapture(0)

        while self._run_flag:
            if not self._pause_flag:
                # 处理速度控制
                if self._speed != 1.0:
                    # 计算需要跳过的帧数
                    self._skip_frames = max(0, int(abs(self._speed)) - 1)

                    # 快退逻辑
                    if self._speed < 0:
                        # 计算目标帧位置
                        target_frame = max(0, self._frame_pos - int(abs(self._speed)))
                        # 设置视频位置
                        self.set_frame_position(target_frame)
                        self._frame_pos = target_frame

                    # 快进逻辑：跳过指定数量的帧
                    for _ in range(self._skip_frames):
                        ret, _ = self.cap.read()
                        if not ret:
                            break
                        self._frame_pos += 1

                # 读取当前帧
                ret, frame = self.cap.read()
                if not ret:  # 视频结束或读取失败
                    break

                # 更新帧位置和进度条
                self._frame_pos += 1
                self._processed_frames += 1

                # 只有在处理实际显示的帧时才更新进度条
                if self._processed_frames % max(1, self._skip_frames + 1) == 0:
                    if self.video_path:
                        progress = int(100 * self._frame_pos / self._total_frames)
                        self.progress_signal.emit(progress)

                # 应用图像处理函数
                processed_frame = self.process_image(frame)
                self.change_pixmap_signal.emit(processed_frame)

                # 控制帧率
                if self._speed > 0:
                    self.msleep(int(1000 / (self._fps * self._speed)))
                else:
                    # 快退时等待时间更短，保持响应
                    self.msleep(int(1000 / (self._fps * abs(self._speed) * 2)))
            else:
                self.msleep(50)  # 暂停状态下减少CPU占用

        # 释放资源并发送结束信号
        self.cap.release()
        self.finished_signal.emit()

    def process_image(self, frame):
        """处理当前帧"""
        # 应用图像处理函数
        return self.processing_function(frame)

    def stop(self):
        """停止线程运行"""
        self._run_flag = False
        self.wait()

    def pause(self):
        """暂停视频播放"""
        self._pause_flag = True

    def resume(self):
        """恢复视频播放"""
        self._pause_flag = False

    def set_speed(self, speed):
        """设置播放速度"""
        self._speed = speed

    def set_frame_position(self, frame_number):
        """跳转到指定帧位置（处理视频文件，摄像头不支持）"""
        if self.video_path and self._total_frames > 0:
            frame_number = max(0, min(frame_number, self._total_frames - 1))
            self.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
            self._frame_pos = frame_number
            # 读取并发送目标帧
            ret, frame = self.cap.read()
            if ret:
                processed_frame = self.process_image(frame)
                self.change_pixmap_signal.emit(processed_frame)
            # 更新进度条
            self.progress_signal.emit(int(100 * frame_number / self._total_frames))


# 主窗口类
class ImageVideoProcessor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.current_image = None
        self.current_video_thread = None
        self.is_video_playing = False
        self.video_position = 0  # 视频当前位置
        self.face_cascade = cv2.CascadeClassifier(
            cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
        )
        self.video_speed = 1.0  # 默认播放速度
        self.video_duration = 0  # 视频总时长（秒）
        self.original_image = None  # 存储原始图像副本
        self.is_cropping = False  # 裁剪模式标志
        self.crop_start = None  # 裁剪起始点
        self.crop_end = None  # 裁剪结束点
        self.images_to_stitch = []  # 图像拼接列表
        self.crop_preview = False  # 裁剪预览标志

    def init_ui(self):
        """初始化用户界面"""
        self.setWindowTitle('图像处理与视频分析平台')
        self.setGeometry(100, 50, 1920, 1200)  # x=100, y=50, 宽度=1920, 高度=1200

        # 创建菜单栏
        self.create_menu_bar()

        # 创建工具栏
        self.create_tool_bar()

        # 创建中央部件
        self.central_widget = QTabWidget()
        self.setCentralWidget(self.central_widget)

        # 添加图像和视频处理选项卡
        self.image_tab = QWidget()
        self.video_tab = QWidget()
        self.central_widget.addTab(self.image_tab, '图像处理')
        self.central_widget.addTab(self.video_tab, '视频处理')

        # 设置图像选项卡
        self.setup_image_tab()

        # 设置视频选项卡（增强版）
        self.setup_video_tab()

        # 创建状态栏
        self.statusBar().showMessage('就绪')
        self.showMaximized()  # 最大化窗口

    def create_menu_bar(self):
        """创建菜单栏"""
        menubar = self.menuBar()

        # 文件菜单
        file_menu = menubar.addMenu('文件')

        # 打开图像动作
        open_image_action = QAction('打开图像', self)
        open_image_action.setShortcut('Ctrl+I')
        open_image_action.triggered.connect(self.open_image)
        file_menu.addAction(open_image_action)

        # 打开视频动作
        open_video_action = QAction('打开视频', self)
        open_video_action.setShortcut('Ctrl+V')
        open_video_action.triggered.connect(self.open_video)
        file_menu.addAction(open_video_action)

        # 保存图像动作
        save_image_action = QAction('保存图像', self)
        save_image_action.setShortcut('Ctrl+S')
        save_image_action.triggered.connect(self.save_image)
        file_menu.addAction(save_image_action)

        file_menu.addSeparator()

        # 退出动作
        exit_action = QAction('退出', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 帮助菜单
        help_menu = menubar.addMenu('帮助')

        # 关于动作
        about_action = QAction('关于', self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)

    def create_tool_bar(self):
        """创建工具栏"""
        toolbar = QToolBar('工具栏')
        self.addToolBar(toolbar)

        # 添加图像工具按钮
        open_image_btn = QPushButton('打开图像')
        open_image_btn.clicked.connect(self.open_image)
        toolbar.addWidget(open_image_btn)

        save_image_btn = QPushButton('保存图像')
        save_image_btn.clicked.connect(self.save_image)
        toolbar.addWidget(save_image_btn)

        toolbar.addSeparator()

        # 添加视频工具按钮
        open_video_btn = QPushButton('打开视频')
        open_video_btn.clicked.connect(self.open_video)
        toolbar.addWidget(open_video_btn)

    def setup_image_tab(self):
        """设置图像处理选项卡"""
        # 创建主布局
        main_layout = QHBoxLayout(self.image_tab)

        # 创建左侧控制面板
        control_panel = QWidget()
        control_layout = QVBoxLayout(control_panel)
        control_panel.setMaximumWidth(300)

        # 图像加载组
        load_group = QGroupBox('图像加载')
        load_layout = QVBoxLayout(load_group)

        load_btn = QPushButton('加载图像')
        load_btn.clicked.connect(self.open_image)
        load_layout.addWidget(load_btn)

        control_layout.addWidget(load_group)

        # 图像处理组
        process_group = QGroupBox('图像处理')
        process_layout = QGridLayout(process_group)

        # 无处理按钮（默认选中，占据第一行两列）
        self.no_process_btn = QPushButton('无处理')
        self.no_process_btn.setCheckable(True)  # 设置为可选中状态
        self.no_process_btn.setChecked(True)  # 默认选中“无处理”
        self.no_process_btn.clicked.connect(lambda: self.process_image('none'))
        process_layout.addWidget(self.no_process_btn, 0, 0, 1, 2)  # 跨2列显示

        # 灰度转换按钮（第1行第0列，点击时取消“无处理”选中）
        gray_btn = QPushButton('灰度转换')
        gray_btn.clicked.connect(lambda: [self.process_image('gray'), self.no_process_btn.setChecked(False)])
        process_layout.addWidget(gray_btn, 1, 0)

        # 边缘检测按钮（第1行第1列，点击时取消“无处理”选中）
        edge_btn = QPushButton('边缘检测')
        edge_btn.clicked.connect(lambda: [self.process_image('edge'), self.no_process_btn.setChecked(False)])
        process_layout.addWidget(edge_btn, 1, 1)

        # 模糊处理按钮（第2行第0列，点击时取消“无处理”选中）
        blur_btn = QPushButton('模糊处理')
        blur_btn.clicked.connect(lambda: [self.process_image('blur'), self.no_process_btn.setChecked(False)])
        process_layout.addWidget(blur_btn, 2, 0)

        # 锐化处理按钮（第2行第1列，点击时取消“无处理”选中）
        sharpen_btn = QPushButton('锐化处理')
        sharpen_btn.clicked.connect(lambda: [self.process_image('sharpen'), self.no_process_btn.setChecked(False)])
        process_layout.addWidget(sharpen_btn, 2, 1)

        # 二值化按钮（第3行，跨两列，点击时取消“无处理”选中）
        thresh_btn = QPushButton('二值化')
        thresh_layout = QHBoxLayout()
        self.thresh_value = QSlider(Qt.Horizontal)
        self.thresh_value.setRange(0, 255)
        self.thresh_value.setValue(127)
        thresh_layout.addWidget(self.thresh_value)
        thresh_btn.clicked.connect(lambda: [self.process_image('threshold'), self.no_process_btn.setChecked(False)])
        thresh_layout.addWidget(thresh_btn)
        process_layout.addLayout(thresh_layout, 3, 0, 1, 2)

        control_layout.addWidget(process_group)

        # 形状识别组
        shape_group = QGroupBox('形状识别')
        shape_layout = QVBoxLayout(shape_group)

        # 人脸检测按钮
        face_btn = QPushButton('人脸检测')
        face_btn.clicked.connect(lambda: [self.process_image('face'), self.no_process_btn.setChecked(False)])
        shape_layout.addWidget(face_btn)
        # 圆形检测按钮
        circle_btn = QPushButton('圆形检测')
        circle_btn.clicked.connect(lambda: [self.process_image('circle'), self.no_process_btn.setChecked(False)])
        shape_layout.addWidget(circle_btn)
        # 矩形检测按钮
        rectangle_btn = QPushButton('矩形检测')
        rectangle_btn.clicked.connect(lambda: [self.process_image('rectangle'), self.no_process_btn.setChecked(False)])
        shape_layout.addWidget(rectangle_btn)
        control_layout.addWidget(shape_group)

        # 图像编辑组
        edit_group = QGroupBox('图像编辑')
        edit_layout = QGridLayout(edit_group)

        # 旋转控制
        rotate_label = QLabel('旋转角度:')
        edit_layout.addWidget(rotate_label, 0, 0)

        self.rotate_value = QSlider(Qt.Horizontal)
        self.rotate_value.setRange(-180, 180)
        self.rotate_value.setValue(0)
        edit_layout.addWidget(self.rotate_value, 0, 1)

        rotate_btn = QPushButton('旋转')
        rotate_btn.clicked.connect(lambda: [self.process_image('rotate'), self.no_process_btn.setChecked(False)])
        edit_layout.addWidget(rotate_btn, 0, 2)

        # 缩放控制 - 增强版
        scale_label = QLabel('缩放比例:')
        edit_layout.addWidget(scale_label, 1, 0)

        self.scale_value = QSlider(Qt.Horizontal)
        self.scale_value.setRange(10, 300)  # 10%到300%
        self.scale_value.setValue(100)
        edit_layout.addWidget(self.scale_value, 1, 1)

        # 添加缩放比例数值输入框
        self.scale_input = QLineEdit()
        self.scale_input.setMaximumWidth(60)
        self.scale_input.setText("100")
        self.scale_input.setToolTip("输入缩放百分比")
        edit_layout.addWidget(self.scale_input, 1, 2)

        # 同步滑块和输入框
        self.scale_value.valueChanged.connect(lambda: self.scale_input.setText(str(self.scale_value.value())))
        self.scale_input.editingFinished.connect(self.update_scale_from_input)

        scale_btn = QPushButton('缩放')
        scale_btn.clicked.connect(lambda: [self.process_image('scale'), self.no_process_btn.setChecked(False)])
        edit_layout.addWidget(scale_btn, 2, 0)

        reset_scale_btn = QPushButton('重置')
        reset_scale_btn.clicked.connect(self.reset_scale)
        edit_layout.addWidget(reset_scale_btn, 2, 1)

        # 裁剪控制 - 增强版
        crop_btn = QPushButton('开始裁剪')
        crop_btn.clicked.connect(lambda: [self.start_crop(), self.no_process_btn.setChecked(False)])
        edit_layout.addWidget(crop_btn, 3, 0, 1, 3)  # 跨3列

        control_layout.addWidget(edit_group)

        # 色彩调整组
        color_group = QGroupBox('色彩调整')
        color_layout = QGridLayout(color_group)

        # 亮度控制
        brightness_label = QLabel('亮度:')
        color_layout.addWidget(brightness_label, 0, 0)

        self.brightness_value = QSlider(Qt.Horizontal)
        self.brightness_value.setRange(-100, 100)
        self.brightness_value.setValue(0)
        color_layout.addWidget(self.brightness_value, 0, 1)

        # 对比度控制
        contrast_label = QLabel('对比度:')
        color_layout.addWidget(contrast_label, 1, 0)

        self.contrast_value = QSlider(Qt.Horizontal)
        self.contrast_value.setRange(-100, 100)
        self.contrast_value.setValue(0)
        color_layout.addWidget(self.contrast_value, 1, 1)

        # 饱和度控制（仅对彩色图像有效）
        saturation_label = QLabel('饱和度:')
        color_layout.addWidget(saturation_label, 2, 0)

        self.saturation_value = QSlider(Qt.Horizontal)
        self.saturation_value.setRange(-100, 100)
        self.saturation_value.setValue(0)
        color_layout.addWidget(self.saturation_value, 2, 1)

        # 应用按钮（跨两列）
        color_btn = QPushButton('应用色彩调整')
        color_btn.clicked.connect(lambda: [self.process_image('color'), self.no_process_btn.setChecked(False)])
        color_layout.addWidget(color_btn, 3, 0, 1, 2)

        control_layout.addWidget(color_group)

        # 图像拼接组
        stitch_group = QGroupBox('图像拼接')
        stitch_layout = QVBoxLayout(stitch_group)

        stitch_btn = QPushButton('添加到拼接')
        stitch_btn.clicked.connect(self.add_to_stitch)
        stitch_layout.addWidget(stitch_btn)

        stitch_process_btn = QPushButton('执行拼接')
        stitch_process_btn.clicked.connect(self.stitch_images)
        stitch_layout.addWidget(stitch_process_btn)

        control_layout.addWidget(stitch_group)

        # 添加到主布局
        main_layout.addWidget(control_panel)

        # 创建右侧图像显示区域
        self.image_display = QLabel('请加载图像')
        self.image_display.setAlignment(Qt.AlignCenter)
        self.image_display.setMinimumSize(500, 500)
        main_layout.addWidget(self.image_display, 1)

    def setup_video_tab(self):
        """设置视频处理选项卡（增强版）"""
        # 创建主布局
        main_layout = QVBoxLayout(self.video_tab)

        # 创建视频控制区域
        control_layout = QHBoxLayout()

        # 打开视频按钮
        open_video_btn = QPushButton('打开视频')
        open_video_btn.clicked.connect(self.open_video)
        control_layout.addWidget(open_video_btn)

        # 播放/暂停按钮
        self.play_pause_btn = QPushButton('播放')
        self.play_pause_btn.clicked.connect(self.toggle_play_pause)
        control_layout.addWidget(self.play_pause_btn)

        # 停止按钮
        self.stop_btn = QPushButton('停止')
        self.stop_btn.clicked.connect(self.stop_video)
        self.stop_btn.setEnabled(False)
        control_layout.addWidget(self.stop_btn)

        # 快退按钮
        self.backward_btn = QPushButton('快退(-10s)')
        self.backward_btn.clicked.connect(lambda: self.seek_video(-10))
        self.backward_btn.setEnabled(False)
        control_layout.addWidget(self.backward_btn)

        # 快进按钮
        self.forward_btn = QPushButton('快进(+10s)')
        self.forward_btn.clicked.connect(lambda: self.seek_video(10))
        self.forward_btn.setEnabled(False)
        control_layout.addWidget(self.forward_btn)

        # 速度控制按钮
        self.slow_down_btn = QPushButton('减速')
        self.slow_down_btn.clicked.connect(self.slow_down_video)
        control_layout.addWidget(self.slow_down_btn)

        self.speed_up_btn = QPushButton('加速')
        self.speed_up_btn.clicked.connect(self.speed_up_video)
        control_layout.addWidget(self.speed_up_btn)

        self.normal_speed_btn = QPushButton('正常速度')
        self.normal_speed_btn.clicked.connect(self.normal_speed_video)
        control_layout.addWidget(self.normal_speed_btn)

        # 添加视频处理选择
        self.video_process_combo = QComboBox()
        self.video_process_combo.addItems(['无处理', '灰度', '边缘检测', '人脸检测'])
        self.video_process_combo.currentIndexChanged.connect(self.change_video_processing)
        control_layout.addWidget(self.video_process_combo)

        main_layout.addLayout(control_layout)

        # 创建视频显示区域
        self.video_display = QLabel('请打开视频或选择摄像头')
        self.video_display.setAlignment(Qt.AlignCenter)
        self.video_display.setMinimumSize(640, 480)
        main_layout.addWidget(self.video_display, 1)

        # 创建进度条
        self.video_progress = QSlider(Qt.Horizontal)
        self.video_progress.setRange(0, 100)
        self.video_progress.setValue(0)
        self.video_progress.sliderMoved.connect(self.on_progress_changed)
        self.video_progress.setEnabled(False)
        main_layout.addWidget(self.video_progress)

        # 创建时间标签
        self.time_label = QLabel("00:00/00:00")
        main_layout.addWidget(self.time_label)

        # 创建速度显示标签
        self.speed_label = QLabel("速度: 1.0x")
        main_layout.addWidget(self.speed_label)

    def open_image(self):
        """打开图像文件"""
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getOpenFileName(
            self, "打开图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif);;所有文件 (*)", options=options)

        if file_name:
            self.current_image = cv2.imread(file_name)
            self.original_image = self.current_image.copy()  # 加载时保存原始图像
            self.display_image(self.current_image)
            self.statusBar().showMessage(f'加载图像: {file_name}')
            self.no_process_btn.setChecked(True)  # 加载后默认选中“无处理”
            self.reset_scale()  # 重置缩放比例

    def display_image(self, img):
        """显示图像"""
        if img is not None:
            # 转换为RGB
            if len(img.shape) == 3:
                rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            else:
                rgb_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)

            # 创建QImage
            h, w, ch = rgb_img.shape
            bytes_per_line = ch * w
            q_img = QImage(rgb_img.data, w, h, bytes_per_line, QImage.Format_RGB888)

            # 显示图像
            self.image_display.setPixmap(QPixmap.fromImage(q_img).scaled(
                self.image_display.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def process_image(self, operation):
        """处理图像"""
        if self.current_image is None:
            QMessageBox.warning(self, '警告', '请先加载图像')
            return

        if operation == 'none':
            # 恢复原始图像
            if self.original_image is not None:
                self.current_image = self.original_image.copy()
                self.display_image(self.current_image)
                self.statusBar().showMessage('已恢复原始图像')
            return

        processed_img = None

        if operation == 'gray':
            processed_img = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2GRAY)

        elif operation == 'edge':
            gray = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2GRAY)
            processed_img = cv2.Canny(gray, 100, 200)

        elif operation == 'blur':
            processed_img = cv2.GaussianBlur(self.current_image, (15, 15), 0)

        elif operation == 'sharpen':
            kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
            processed_img = cv2.filter2D(self.current_image, -1, kernel)

        elif operation == 'threshold':
            gray = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2GRAY)
            thresh_value = self.thresh_value.value()
            _, processed_img = cv2.threshold(gray, thresh_value, 255, cv2.THRESH_BINARY)

        elif operation == 'face':
            processed_img = self.current_image.copy()
            gray = cv2.cvtColor(processed_img, cv2.COLOR_BGR2GRAY)
            faces = self.face_cascade.detectMultiScale(gray, 1.1, 4)

            for (x, y, w, h) in faces:
                cv2.rectangle(processed_img, (x, y), (x + w, y + h), (255, 0, 0), 2)

            self.statusBar().showMessage(f'检测到 {len(faces)} 个人脸')

        elif operation == 'circle':
            processed_img = self.current_image.copy()
            gray = cv2.cvtColor(processed_img, cv2.COLOR_BGR2GRAY)
            gray = cv2.medianBlur(gray, 5)

            circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20,
                                       param1=50, param2=30, minRadius=0, maxRadius=0)

            if circles is not None:
                circles = np.uint16(np.around(circles))
                for i in circles[0, :]:
                    # 绘制外圆
                    cv2.circle(processed_img, (i[0], i[1]), i[2], (0, 255, 0), 2)
                    # 绘制圆心
                    cv2.circle(processed_img, (i[0], i[1]), 2, (0, 0, 255), 3)

                self.statusBar().showMessage(f'检测到 {len(circles[0])} 个圆形')

        elif operation == 'rectangle':
            processed_img = self.current_image.copy()
            gray = cv2.cvtColor(processed_img, cv2.COLOR_BGR2GRAY)
            _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

            contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

            rectangles = []
            for contour in contours:
                epsilon = 0.02 * cv2.arcLength(contour, True)
                approx = cv2.approxPolyDP(contour, epsilon, True)

                if len(approx) == 4:
                    rectangles.append(approx)
                    cv2.drawContours(processed_img, [approx], 0, (0, 0, 255), 2)

            self.statusBar().showMessage(f'检测到 {len(rectangles)} 个矩形')

        elif operation == 'rotate':
            # 获取旋转角度
            angle = self.rotate_value.value()
            if angle == 0:
                processed_img = self.current_image
            else:
                # 获取图像尺寸
                (h, w) = self.current_image.shape[:2]
                # 计算旋转中心
                center = (w // 2, h // 2)
                # 定义旋转矩阵
                M = cv2.getRotationMatrix2D(center, angle, 1.0)
                # 执行旋转
                processed_img = cv2.warpAffine(self.current_image, M, (w, h))
                self.statusBar().showMessage(f'图像已旋转 {angle} 度')

        elif operation == 'scale':
            # 获取缩放比例
            try:
                scale_percent = float(self.scale_input.text())
            except ValueError:
                scale_percent = 100.0
                self.scale_input.setText("100")

            # 确保缩放比例在有效范围内
            scale_percent = max(10.0, min(300.0, scale_percent))
            self.scale_input.setText(f"{scale_percent:.1f}")
            self.scale_value.setValue(int(scale_percent))

            scale_factor = scale_percent / 100.0

            if scale_factor == 1.0:
                processed_img = self.current_image
            else:
                # 执行缩放
                processed_img = cv2.resize(self.current_image, None, fx=scale_factor, fy=scale_factor,
                                           interpolation=cv2.INTER_LINEAR)
                self.statusBar().showMessage(f'图像已缩放至 {scale_percent:.1f}%')

        elif operation == 'color':
            # 获取调整值
            brightness = self.brightness_value.value()
            contrast = self.contrast_value.value()
            saturation = self.saturation_value.value()

            # 检查是否有调整
            if brightness == 0 and contrast == 0 and saturation == 0:
                processed_img = self.current_image
                self.statusBar().showMessage('未进行色彩调整')
                return

            # 处理亮度和对比度
            if len(self.current_image.shape) == 3:  # 彩色图像
                # 转换为HSV以调整饱和度
                hsv_img = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2HSV)
                h, s, v = cv2.split(hsv_img)

                # 调整饱和度
                if saturation != 0:
                    sat_factor = 1.0 + (saturation / 100.0)
                    s = np.clip(s * sat_factor, 0, 255).astype(hsv_img.dtype)

                # 合并回HSV并转换回BGR
                hsv_img = cv2.merge([h, s, v])
                bgr_img = cv2.cvtColor(hsv_img, cv2.COLOR_HSV2BGR)
            else:  # 灰度图像
                bgr_img = self.current_image

            # 调整亮度和对比度
            if brightness != 0 or contrast != 0:
                # 应用亮度和对比度调整
                if contrast != 0:
                    # 计算对比度调整因子
                    contrast_factor = (259 * (contrast + 255)) / (255 * (259 - contrast))
                    # 应用对比度调整
                    bgr_img = np.clip(contrast_factor * (bgr_img.astype(np.float32) - 128) + 128, 0, 255).astype(
                        np.uint8)

                # 应用亮度调整
                if brightness != 0:
                    bgr_img = np.clip(bgr_img.astype(np.int16) + brightness, 0, 255).astype(np.uint8)

            processed_img = bgr_img
            self.statusBar().showMessage(f'亮度: {brightness}, 对比度: {contrast}, 饱和度: {saturation}')

        if processed_img is not None:
            self.current_image = processed_img
            self.display_image(processed_img)

    def update_scale_from_input(self):
        """从输入框更新缩放值"""
        try:
            value = float(self.scale_input.text())
            value = max(10.0, min(300.0, value))
            self.scale_value.setValue(int(value))
        except ValueError:
            self.scale_input.setText(str(self.scale_value.value()))

    def reset_scale(self):
        """重置缩放比例为100%"""
        self.scale_value.setValue(100)
        self.scale_input.setText("100")

    def start_crop(self):
        """开始裁剪模式"""
        if self.current_image is None:
            QMessageBox.warning(self, '警告', '请先加载图像')
            return

        self.is_cropping = True
        self.crop_start = None
        self.crop_end = None
        self.temp_crop_img = self.current_image.copy()  # 创建当前图像副本用于预览
        self.image_display.setCursor(Qt.CrossCursor)
        self.statusBar().showMessage('请在图像上拖动鼠标选择裁剪区域')

        # 绑定鼠标事件处理函数
        self.image_display.mousePressEvent = self.on_crop_mouse_press
        self.image_display.mouseMoveEvent = self.on_crop_mouse_move
        self.image_display.mouseReleaseEvent = self.on_crop_mouse_release
        self.display_image(self.current_image)  # 刷新显示原始图像

    def on_crop_mouse_press(self, event):
        """鼠标按下事件处理 - 裁剪模式"""
        if self.is_cropping:
            # 获取显示区域的像素图
            pixmap = self.image_display.pixmap()
            if not pixmap:
                return

            # 获取原始图像尺寸和显示尺寸
            orig_h, orig_w = self.current_image.shape[:2]
            disp_w = pixmap.width()
            disp_h = pixmap.height()

            # 计算缩放比例（考虑宽高比，避免拉伸）
            scale_x = orig_w / disp_w
            scale_y = orig_h / disp_h

            # 将界面坐标转换为原始图像坐标（考虑缩放比例）
            x = event.x() * scale_x
            y = event.y() * scale_y
            self.crop_start = (x, y)

    def on_crop_mouse_move(self, event):
        """鼠标移动事件处理 - 裁剪模式"""
        if self.is_cropping and self.crop_start is not None:
            # 获取显示区域的像素图
            pixmap = self.image_display.pixmap()
            if not pixmap:
                return

            # 获取原始图像尺寸和显示尺寸
            orig_h, orig_w = self.current_image.shape[:2]
            disp_w = pixmap.width()
            disp_h = pixmap.height()

            # 计算缩放比例
            scale_x = orig_w / disp_w
            scale_y = orig_h / disp_h

            # 将界面坐标转换为原始图像坐标
            x = event.x() * scale_x
            y = event.y() * scale_y
            self.crop_end = (x, y)
            self.display_crop_rect()  # 实时绘制裁剪框

    def display_crop_rect(self):
        """显示裁剪矩形框"""
        if self.current_image is None or self.crop_start is None or self.crop_end is None:
            return

        # 创建临时图像用于显示裁剪框（使用原始图像副本）
        temp_img = self.temp_crop_img.copy()
        orig_h, orig_w = temp_img.shape[:2]

        # 获取显示区域的像素图尺寸
        pixmap = self.image_display.pixmap()
        if not pixmap:
            return
        disp_w = pixmap.width()
        disp_h = pixmap.height()

        # 计算反向缩放比例（原始坐标转显示坐标）
        scale_x = disp_w / orig_w
        scale_y = disp_h / orig_h

        # 将原始坐标转换为显示坐标以绘制矩形
        x1, y1 = self.crop_start
        x2, y2 = self.crop_end
        x_min_disp = max(0, min(int(x1 * scale_x), disp_w))
        y_min_disp = max(0, min(int(y1 * scale_y), disp_h))
        x_max_disp = max(0, min(int(x2 * scale_x), disp_w))
        y_max_disp = max(0, min(int(y2 * scale_y), disp_h))

        # 绘制绿色矩形框（线宽2像素）
        cv2.rectangle(temp_img, (x_min_disp, y_min_disp), (x_max_disp, y_max_disp), (0, 255, 0), 2)

        # 转换为QImage并显示
        if len(temp_img.shape) == 3:
            rgb_img = cv2.cvtColor(temp_img, cv2.COLOR_BGR2RGB)
        else:
            rgb_img = cv2.cvtColor(temp_img, cv2.COLOR_GRAY2RGB)

        h, w, ch = rgb_img.shape
        bytes_per_line = ch * w
        q_img = QImage(rgb_img.data, w, h, bytes_per_line, QImage.Format_RGB888)
        self.image_display.setPixmap(QPixmap.fromImage(q_img))

    def on_crop_mouse_release(self, event):
        """鼠标释放事件处理 - 裁剪模式"""
        if self.is_cropping and self.crop_start is not None and self.crop_end is not None:
            x1, y1 = self.crop_start
            x2, y2 = self.crop_end

            # 确保坐标有效（x1 < x2, y1 < y2）
            x_min = max(0, min(x1, x2))
            y_min = max(0, min(y1, y2))
            x_max = min(self.current_image.shape[1], max(x1, x2))
            y_max = min(self.current_image.shape[0], max(y1, y2))

            if x_min < x_max and y_min < y_max:
                # 执行裁剪
                self.current_image = self.current_image[int(y_min):int(y_max), int(x_min):int(x_max)]
                self.display_image(self.current_image)
                self.statusBar().showMessage(
                    f'图像已裁剪: ({int(x_min)}, {int(y_min)}) 到 ({int(x_max)}, {int(y_max)})')
            else:
                QMessageBox.warning(self, '警告', '无效的裁剪区域（坐标范围错误）')

            self.is_cropping = False
            self.image_display.setCursor(Qt.ArrowCursor)
            self.temp_crop_img = self.current_image.copy()  # 更新临时图像

    def add_to_stitch(self):
        """添加当前图像到拼接列表"""
        if self.current_image is None:
            QMessageBox.warning(self, '警告', '请先加载图像')
            return

        self.images_to_stitch.append(self.current_image.copy())
        self.statusBar().showMessage(f'已添加图像到拼接列表，当前列表长度: {len(self.images_to_stitch)}')

    def stitch_images(self):
        """执行图像拼接"""
        if len(self.images_to_stitch) < 2:
            QMessageBox.warning(self, '警告', '请至少添加两张图像到拼接列表')
            return

        # 创建拼接器
        stitcher = cv2.Stitcher_create() if hasattr(cv2, 'Stitcher_create') else cv2.createStitcher()

        # 执行拼接
        status, stitched = stitcher.stitch(self.images_to_stitch)

        if status == cv2.STITCHER_OK:
            self.current_image = stitched
            self.display_image(stitched)
            self.statusBar().showMessage('图像拼接成功')
        else:
            self.statusBar().showMessage(f'图像拼接失败，错误码: {status}')
            QMessageBox.warning(self, '警告', f'图像拼接失败，错误码: {status}')

    def save_image(self):
        """保存当前图像"""
        if self.current_image is None:
            QMessageBox.warning(self, '警告', '请先加载或处理图像')
            return

        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getSaveFileName(
            self, "保存图像", "", "PNG文件 (*.png);;JPEG文件 (*.jpg);;所有文件 (*)", options=options)

        if file_name:
            try:
                # 确保文件扩展名为有效格式
                if not file_name.lower().endswith(('.png', '.jpg', '.jpeg')):
                    file_name += '.png'

                # 保存图像
                cv2.imwrite(file_name, self.current_image)
                self.statusBar().showMessage(f'图像已保存至: {file_name}')
            except Exception as e:
                QMessageBox.critical(self, '错误', f'保存图像失败: {str(e)}')
                self.statusBar().showMessage('保存图像失败')

    def open_video(self):
        """打开视频文件或启动摄像头"""
        # 停止当前播放的视频
        if self.current_video_thread is not None:
            self.current_video_thread.stop()
            self.current_video_thread = None

        # 询问用户是打开文件还是使用摄像头
        choice = QMessageBox.question(
            self, '选择视频源', '你想打开视频文件还是使用摄像头?\n(Open=视频文件, Yes=摄像头)',
            QMessageBox.Open | QMessageBox.Cancel | QMessageBox.Yes, QMessageBox.Open
        )

        if choice == QMessageBox.Cancel:
            return

        video_path = None
        if choice == QMessageBox.Open:
            # 打开视频文件
            options = QFileDialog.Options()
            video_path, _ = QFileDialog.getOpenFileName(
                self, "打开视频", "", "视频文件 (*.mp4 *.avi *.mov *.mkv);;所有文件 (*)", options=options)

            if not video_path:
                return

        # 创建并启动视频线程
        self.current_video_thread = VideoThread(video_path)
        self.current_video_thread.change_pixmap_signal.connect(self.update_video_frame)
        self.current_video_thread.finished_signal.connect(self.video_finished)
        self.current_video_thread.progress_signal.connect(self.update_video_progress)
        self.current_video_thread.start()

        # 更新UI状态
        self.is_video_playing = True
        self.play_pause_btn.setText('暂停')
        self.stop_btn.setEnabled(True)
        self.backward_btn.setEnabled(True)
        self.forward_btn.setEnabled(True)
        self.video_progress.setEnabled(True)

        # 获取视频信息
        if video_path:
            cap = cv2.VideoCapture(video_path)
            self.video_duration = int(cap.get(cv2.CAP_PROP_FRAME_COUNT) / cap.get(cv2.CAP_PROP_FPS)) if cap.get(
                cv2.CAP_PROP_FPS) != 0 else 0
            cap.release()
            self.update_time_label()

        # 设置初始视频处理函数
        self.change_video_processing(0)

    def update_video_frame(self, frame):
        """更新视频帧显示"""
        # 转换为RGB
        rgb_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        # 创建QImage
        h, w, ch = rgb_img.shape
        bytes_per_line = ch * w
        q_img = QImage(rgb_img.data, w, h, bytes_per_line, QImage.Format_RGB888)

        # 显示图像
        self.video_display.setPixmap(QPixmap.fromImage(q_img).scaled(
            self.video_display.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

        # 更新时间标签
        if self.current_video_thread and self.current_video_thread._total_frames > 0:
            current_time = int(self.current_video_thread._frame_pos / self.current_video_thread._fps)
            self.time_label.setText(f"{self.format_time(current_time)}/{self.format_time(self.video_duration)}")

    def update_video_progress(self, progress):
        """更新视频进度条"""
        self.video_progress.setValue(progress)

    def update_time_label(self):
        """更新时间标签"""
        if self.current_video_thread and self.current_video_thread._total_frames > 0:
            current_time = int(self.current_video_thread._frame_pos / self.current_video_thread._fps)
            self.time_label.setText(f"{self.format_time(current_time)}/{self.format_time(self.video_duration)}")

    def format_time(self, seconds):
        """格式化时间为 MM:SS 格式"""
        minutes, seconds = divmod(seconds, 60)
        return f"{minutes:02d}:{seconds:02d}"

    def toggle_play_pause(self):
        """切换播放/暂停状态"""
        if self.current_video_thread is None:
            return

        if self.is_video_playing:
            self.current_video_thread.pause()
            self.play_pause_btn.setText('播放')
        else:
            self.current_video_thread.resume()
            self.play_pause_btn.setText('暂停')

        self.is_video_playing = not self.is_video_playing

    def stop_video(self):
        """停止视频播放"""
        if self.current_video_thread is not None:
            self.current_video_thread.stop()
            self.current_video_thread = None

        self.is_video_playing = False
        self.play_pause_btn.setText('播放')
        self.stop_btn.setEnabled(False)
        self.backward_btn.setEnabled(False)
        self.forward_btn.setEnabled(False)
        self.video_progress.setEnabled(False)
        self.video_progress.setValue(0)
        self.time_label.setText("00:00/00:00")
        self.video_display.setText('请打开视频或选择摄像头')

    def slow_down_video(self):
        """减慢视频播放速度"""
        if self.current_video_thread is None:
            return

        self.video_speed = max(0.1, self.video_speed * 0.5)
        self.current_video_thread.set_speed(self.video_speed)
        self.speed_label.setText(f"速度: {self.video_speed:.1f}x")

    def speed_up_video(self):
        """加快视频播放速度"""
        if self.current_video_thread is None:
            return

        self.video_speed = min(8.0, self.video_speed * 2)
        self.current_video_thread.set_speed(self.video_speed)
        self.speed_label.setText(f"速度: {self.video_speed:.1f}x")

    def normal_speed_video(self):
        """恢复正常视频播放速度"""
        if self.current_video_thread is None:
            return

        self.video_speed = 1.0
        self.current_video_thread.set_speed(self.video_speed)
        self.speed_label.setText(f"速度: {self.video_speed:.1f}x")

    def seek_video(self, seconds):
        """在视频中前进或后退指定秒数"""
        if self.current_video_thread is None:
            return

        video_thread = self.current_video_thread
        if not video_thread.video_path:  # 摄像头不支持跳转
            return

        fps = video_thread._fps
        current_frame = video_thread._frame_pos
        target_frame = current_frame + int(fps * seconds)
        target_frame = max(0, min(target_frame, video_thread._total_frames - 1))
        video_thread.set_frame_position(target_frame)

        # 更新时间显示
        current_time = int(target_frame / fps)
        self.time_label.setText(f"{self.format_time(current_time)}/{self.format_time(self.video_duration)}")

    def on_progress_changed(self, position):
        """进度条位置改变时响应"""
        if self.current_video_thread is not None and self.current_video_thread.video_path:
            self.current_video_thread.set_frame_position(int(self.current_video_thread._total_frames * position / 100))

    def change_video_processing(self, index):
        """改变视频处理方式"""
        if self.current_video_thread is None:
            return

        if index == 0:  # 无处理
            self.current_video_thread.processing_function = lambda frame: frame
        elif index == 1:  # 灰度
            self.current_video_thread.processing_function = lambda frame: cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        elif index == 2:  # 边缘检测
            self.current_video_thread.processing_function = lambda frame: cv2.Canny(frame, 100, 200)
        elif index == 3:  # 人脸检测
            self.current_video_thread.processing_function = self.process_face_detection

    def process_face_detection(self, frame):
        """处理人脸检测"""
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = self.face_cascade.detectMultiScale(gray, 1.1, 4)

        for (x, y, w, h) in faces:
            cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)

        return frame

    def video_finished(self):
        """视频播放结束时调用"""
        self.is_video_playing = False
        self.play_pause_btn.setText('播放')
        self.statusBar().showMessage('视频播放完毕')

    def show_about(self):
        """显示关于对话框"""
        QMessageBox.about(self, '关于图像处理与视频分析平台',
                          '图像处理与视频分析平台\n\n'
                          '版本: 1.0\n\n'
                          '这是一个基于OpenCV和PyQt5的图像处理与视频分析工具，\n'
                          '支持图像增强、形状识别、视频播放和处理等功能。')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 设置全局字体以确保中文显示正常
    font = QFont("SimHei")
    app.setFont(font)
    window = ImageVideoProcessor()
    window.show()
    sys.exit(app.exec_())